{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default_exp data.unwindowed"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Unwindowed datasets\n",
    "\n",
    "> This functionality will allow you to create a dataset that applies sliding windows to the input data on the fly. This heavily reduces the size of the input data files, as only the original, unwindowed data needs to be stored."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* I'd like to thank both Thomas Capelle (https://github.com/tcapelle)  and Xander Dunn (https://github.com/xanderdunn) for their contributions to make this code possible. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from tsai.imports import *\n",
    "from tsai.utils import *\n",
    "from tsai.data.validation import *\n",
    "from tsai.data.core import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class TSUnwindowedDataset():\n",
    "    _types = TSTensor, TSLabelTensor\n",
    "    def __init__(self, X, y=None, y_func=None, window_size=1, stride=1, drop_start=0, drop_end=0, seq_first=True, **kwargs):\n",
    "        store_attr()\n",
    "        if X.ndim == 1: X = np.expand_dims(X, 1)\n",
    "        shape = X.shape\n",
    "        assert len(shape) == 2\n",
    "        if seq_first: \n",
    "            seq_len = shape[0]\n",
    "        else: \n",
    "            seq_len = shape[-1]\n",
    "        max_time = seq_len - window_size + 1 - drop_end\n",
    "        assert max_time > 0, 'you need to modify either window_size or drop_end as they are larger than seq_len'\n",
    "        self.all_idxs = np.expand_dims(np.arange(drop_start, max_time, step=stride), 0).T\n",
    "        self.window_idxs = np.expand_dims(np.arange(window_size), 0)\n",
    "        if 'split' in kwargs: self.split = kwargs['split']\n",
    "        else: self.split = None\n",
    "        self.n_inp = 1\n",
    "        if y is None: self.loss_func = MSELossFlat()\n",
    "        else: \n",
    "            _,yb=self[:2]\n",
    "            if (is_listy(yb[0]) and isinstance(yb[0][0], Integral)) or isinstance(yb[0], Integral): self.loss_func = CrossEntropyLossFlat()\n",
    "            else: self.loss_func = MSELossFlat()\n",
    "\n",
    "    def __len__(self):\n",
    "        if self.split is not None: \n",
    "            return len(self.split)\n",
    "        else: \n",
    "            return len(self.all_idxs)\n",
    "\n",
    "    def __getitem__(self, idxs):\n",
    "        if self.split is not None:\n",
    "            idxs = self.split[idxs]\n",
    "        widxs = self.all_idxs[idxs] + self.window_idxs\n",
    "        if self.seq_first:\n",
    "            xb = self.X[widxs]\n",
    "            if xb.ndim == 3: xb = xb.transpose(0,2,1)\n",
    "            else: xb = np.expand_dims(xb, 1)\n",
    "        else:\n",
    "            xb = self.X[:, widxs].transpose(1,0,2)\n",
    "        if self.y is None:\n",
    "            return (self._types[0](xb),)\n",
    "        else:\n",
    "            yb = self.y[widxs]\n",
    "            if self.y_func is not None: \n",
    "                yb = self.y_func(yb)\n",
    "            return (self._types[0](xb), self._types[1](yb))\n",
    "    @property\n",
    "    def vars(self):\n",
    "        s = self[0][0] if not isinstance(self[0][0], tuple) else self[0][0][0]\n",
    "        return s.shape[-2]\n",
    "    @property\n",
    "    def len(self): \n",
    "        s = self[0][0] if not isinstance(self[0][0], tuple) else self[0][0][0]\n",
    "        return s.shape[-1]    \n",
    "\n",
    "\n",
    "class TSUnwindowedDatasets(FilteredBase):\n",
    "    def __init__(self, dataset, splits):\n",
    "        store_attr()\n",
    "    def subset(self, i):\n",
    "        return type(self.dataset)(self.dataset.X, y=self.dataset.y, y_func=self.dataset.y_func, window_size=self.dataset.window_size,\n",
    "                                  stride=self.dataset.stride, drop_start=self.dataset.drop_start, drop_end=self.dataset.drop_end, \n",
    "                                  seq_first=self.dataset.seq_first, split=self.splits[i])\n",
    "    @property\n",
    "    def train(self): \n",
    "        return self.subset(0)\n",
    "    @property\n",
    "    def valid(self): \n",
    "        return self.subset(1)\n",
    "    def __getitem__(self, i): return self.subset(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def y_func(y): return y.astype('float').mean(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This approach works with both univariate and multivariate data.\n",
    "\n",
    "* Univariate: we'll use a simple array with 20 values, one with the seq_len first (X0), the other with seq_len second (X1).\n",
    "* Multivariate: we'll use 2 time series arrays, one with the seq_len first (X2), the other with seq_len second (X3). No sliding window has been applied to them yet. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((20,),\n",
       " array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,\n",
       "        17, 18, 19]),\n",
       " (1, 20),\n",
       " array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,\n",
       "         16, 17, 18, 19]]))"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Univariate\n",
    "X0 = np.arange(20)\n",
    "X1 = np.arange(20).reshape(1, -1)\n",
    "X0.shape, X0, X1.shape, X1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((20, 3),\n",
       " (3, 20),\n",
       " array([[   0,    0,    0],\n",
       "        [   1,   10,  100],\n",
       "        [   2,   20,  200],\n",
       "        [   3,   30,  300],\n",
       "        [   4,   40,  400],\n",
       "        [   5,   50,  500],\n",
       "        [   6,   60,  600],\n",
       "        [   7,   70,  700],\n",
       "        [   8,   80,  800],\n",
       "        [   9,   90,  900],\n",
       "        [  10,  100, 1000],\n",
       "        [  11,  110, 1100],\n",
       "        [  12,  120, 1200],\n",
       "        [  13,  130, 1300],\n",
       "        [  14,  140, 1400],\n",
       "        [  15,  150, 1500],\n",
       "        [  16,  160, 1600],\n",
       "        [  17,  170, 1700],\n",
       "        [  18,  180, 1800],\n",
       "        [  19,  190, 1900]]),\n",
       " array([[   0,    1,    2,    3,    4,    5,    6,    7,    8,    9,   10,\n",
       "           11,   12,   13,   14,   15,   16,   17,   18,   19],\n",
       "        [   0,   10,   20,   30,   40,   50,   60,   70,   80,   90,  100,\n",
       "          110,  120,  130,  140,  150,  160,  170,  180,  190],\n",
       "        [   0,  100,  200,  300,  400,  500,  600,  700,  800,  900, 1000,\n",
       "         1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900]]))"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Multivariate\n",
    "X2 = np.arange(20).reshape(-1,1)*np.array([1, 10, 100]).reshape(1,-1)\n",
    "X3 = np.arange(20).reshape(1,-1)*np.array([1, 10, 100]).reshape(-1,1)\n",
    "X2.shape, X3.shape, X2, X3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, instead of applying SlidingWindow to create and save the time series that can be consumed by a time series model, we can use a dataset that creates the data on the fly. In this way we avoid the need to create and save large files. This approach is also useful when you want to test different sliding window sizes, as otherwise you would need to create files for every size you want to test.The dataset will create the samples correctly formatted and ready to be passed on to a time series architecture."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(TSTensor(samples:8, vars:1, len:5),\n",
       " tensor([[[ 0,  1,  2,  3,  4]],\n",
       " \n",
       "         [[ 2,  3,  4,  5,  6]],\n",
       " \n",
       "         [[ 4,  5,  6,  7,  8]],\n",
       " \n",
       "         [[ 6,  7,  8,  9, 10]],\n",
       " \n",
       "         [[ 8,  9, 10, 11, 12]],\n",
       " \n",
       "         [[10, 11, 12, 13, 14]],\n",
       " \n",
       "         [[12, 13, 14, 15, 16]],\n",
       " \n",
       "         [[14, 15, 16, 17, 18]]]),\n",
       " TSTensor(samples:8, vars:1, len:5),\n",
       " tensor([[[ 0,  1,  2,  3,  4]],\n",
       " \n",
       "         [[ 2,  3,  4,  5,  6]],\n",
       " \n",
       "         [[ 4,  5,  6,  7,  8]],\n",
       " \n",
       "         [[ 6,  7,  8,  9, 10]],\n",
       " \n",
       "         [[ 8,  9, 10, 11, 12]],\n",
       " \n",
       "         [[10, 11, 12, 13, 14]],\n",
       " \n",
       "         [[12, 13, 14, 15, 16]],\n",
       " \n",
       "         [[14, 15, 16, 17, 18]]]))"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wds0 = TSUnwindowedDataset(X0, window_size=5, stride=2, seq_first=True)[:][0]\n",
    "wds1 = TSUnwindowedDataset(X1, window_size=5, stride=2, seq_first=False)[:][0]\n",
    "test_eq(wds0, wds1)\n",
    "wds0, wds0.data, wds1, wds1.data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(TSTensor(samples:8, vars:3, len:5),\n",
       " TSTensor(samples:8, vars:3, len:5),\n",
       " tensor([[[   0,    1,    2,    3,    4],\n",
       "          [   0,   10,   20,   30,   40],\n",
       "          [   0,  100,  200,  300,  400]],\n",
       " \n",
       "         [[   2,    3,    4,    5,    6],\n",
       "          [  20,   30,   40,   50,   60],\n",
       "          [ 200,  300,  400,  500,  600]],\n",
       " \n",
       "         [[   4,    5,    6,    7,    8],\n",
       "          [  40,   50,   60,   70,   80],\n",
       "          [ 400,  500,  600,  700,  800]],\n",
       " \n",
       "         [[   6,    7,    8,    9,   10],\n",
       "          [  60,   70,   80,   90,  100],\n",
       "          [ 600,  700,  800,  900, 1000]],\n",
       " \n",
       "         [[   8,    9,   10,   11,   12],\n",
       "          [  80,   90,  100,  110,  120],\n",
       "          [ 800,  900, 1000, 1100, 1200]],\n",
       " \n",
       "         [[  10,   11,   12,   13,   14],\n",
       "          [ 100,  110,  120,  130,  140],\n",
       "          [1000, 1100, 1200, 1300, 1400]],\n",
       " \n",
       "         [[  12,   13,   14,   15,   16],\n",
       "          [ 120,  130,  140,  150,  160],\n",
       "          [1200, 1300, 1400, 1500, 1600]],\n",
       " \n",
       "         [[  14,   15,   16,   17,   18],\n",
       "          [ 140,  150,  160,  170,  180],\n",
       "          [1400, 1500, 1600, 1700, 1800]]]),\n",
       " tensor([[[   0,    1,    2,    3,    4],\n",
       "          [   0,   10,   20,   30,   40],\n",
       "          [   0,  100,  200,  300,  400]],\n",
       " \n",
       "         [[   2,    3,    4,    5,    6],\n",
       "          [  20,   30,   40,   50,   60],\n",
       "          [ 200,  300,  400,  500,  600]],\n",
       " \n",
       "         [[   4,    5,    6,    7,    8],\n",
       "          [  40,   50,   60,   70,   80],\n",
       "          [ 400,  500,  600,  700,  800]],\n",
       " \n",
       "         [[   6,    7,    8,    9,   10],\n",
       "          [  60,   70,   80,   90,  100],\n",
       "          [ 600,  700,  800,  900, 1000]],\n",
       " \n",
       "         [[   8,    9,   10,   11,   12],\n",
       "          [  80,   90,  100,  110,  120],\n",
       "          [ 800,  900, 1000, 1100, 1200]],\n",
       " \n",
       "         [[  10,   11,   12,   13,   14],\n",
       "          [ 100,  110,  120,  130,  140],\n",
       "          [1000, 1100, 1200, 1300, 1400]],\n",
       " \n",
       "         [[  12,   13,   14,   15,   16],\n",
       "          [ 120,  130,  140,  150,  160],\n",
       "          [1200, 1300, 1400, 1500, 1600]],\n",
       " \n",
       "         [[  14,   15,   16,   17,   18],\n",
       "          [ 140,  150,  160,  170,  180],\n",
       "          [1400, 1500, 1600, 1700, 1800]]]))"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wds2 = TSUnwindowedDataset(X2, window_size=5, stride=2, seq_first=True)[:][0]\n",
    "wds3 = TSUnwindowedDataset(X3, window_size=5, stride=2, seq_first=False)[:][0]\n",
    "test_eq(wds2, wds3)\n",
    "wds2, wds3, wds2.data, wds3.data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.notebook.save_checkpoint();"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Converted 000_utils.ipynb.\n",
      "Converted 000b_data.validation.ipynb.\n",
      "Converted 000c_data.preparation.ipynb.\n",
      "Converted 001_data.external.ipynb.\n",
      "Converted 002_data.core.ipynb.\n",
      "Converted 002b_data.unwindowed.ipynb.\n",
      "Converted 002c_data.metadatasets.ipynb.\n",
      "Converted 003_data.preprocessing.ipynb.\n",
      "Converted 003b_data.transforms.ipynb.\n",
      "Converted 003c_data.mixed_augmentation.ipynb.\n",
      "Converted 003d_data.image.ipynb.\n",
      "Converted 003e_data.features.ipynb.\n",
      "Converted 005_data.tabular.ipynb.\n",
      "Converted 006_data.mixed.ipynb.\n",
      "Converted 051_metrics.ipynb.\n",
      "Converted 052_learner.ipynb.\n",
      "Converted 052b_tslearner.ipynb.\n",
      "Converted 053_optimizer.ipynb.\n",
      "Converted 060_callback.core.ipynb.\n",
      "Converted 061_callback.noisy_student.ipynb.\n",
      "Converted 062_callback.gblend.ipynb.\n",
      "Converted 063_callback.MVP.ipynb.\n",
      "Converted 064_callback.PredictionDynamics.ipynb.\n",
      "Converted 100_models.layers.ipynb.\n",
      "Converted 100b_models.utils.ipynb.\n",
      "Converted 100c_models.explainability.ipynb.\n",
      "Converted 101_models.ResNet.ipynb.\n",
      "Converted 101b_models.ResNetPlus.ipynb.\n",
      "Converted 102_models.InceptionTime.ipynb.\n",
      "Converted 102b_models.InceptionTimePlus.ipynb.\n",
      "Converted 103_models.MLP.ipynb.\n",
      "Converted 103b_models.FCN.ipynb.\n",
      "Converted 103c_models.FCNPlus.ipynb.\n",
      "Converted 104_models.ResCNN.ipynb.\n",
      "Converted 105_models.RNN.ipynb.\n",
      "Converted 105_models.RNNPlus.ipynb.\n",
      "Converted 106_models.XceptionTime.ipynb.\n",
      "Converted 106b_models.XceptionTimePlus.ipynb.\n",
      "Converted 107_models.RNN_FCN.ipynb.\n",
      "Converted 107b_models.RNN_FCNPlus.ipynb.\n",
      "Converted 108_models.TransformerModel.ipynb.\n",
      "Converted 108b_models.TST.ipynb.\n",
      "Converted 108c_models.TSTPlus.ipynb.\n",
      "Converted 109_models.OmniScaleCNN.ipynb.\n",
      "Converted 110_models.mWDN.ipynb.\n",
      "Converted 111_models.ROCKET.ipynb.\n",
      "Converted 111b_models.MINIROCKET.ipynb.\n",
      "Converted 112_models.XResNet1d.ipynb.\n",
      "Converted 112b_models.XResNet1dPlus.ipynb.\n",
      "Converted 113_models.TCN.ipynb.\n",
      "Converted 114_models.XCM.ipynb.\n",
      "Converted 114b_models.XCMPlus.ipynb.\n",
      "Converted 120_models.TabModel.ipynb.\n",
      "Converted 121_models.TabTransformer.ipynb.\n",
      "Converted 122_models.TabFusionTransformer.ipynb.\n",
      "Converted 130_models.MultiInputNet.ipynb.\n",
      "Converted 140_models.misc.ipynb.\n",
      "Converted 900_tutorials.ipynb.\n",
      "Converted index.ipynb.\n",
      "\n",
      "\n",
      "Checking folder: /Users/nacho/Documents/Machine_Learning/Jupyter_Notebooks/tsai/tsai\n",
      "Correct conversion! 😃\n",
      "Total time elapsed 103 s\n",
      "Monday 12/04/21 11:52:24 CEST\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" autoplay=\"autoplay\">\n",
       "                    <source src=\"data:audio/wav;base64,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\" type=\"audio/wav\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#hide\n",
    "out = create_scripts(); beep(out)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
